﻿//
// Command.cs
//
// Authors:
//  Kenny York <kennyyork@gmail.com>
//
// Copyright (C) 2009 Kenny York
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace clidotnet
{
    public abstract class CommandBase : ICommand
    {
        /// <summary>
        /// A list of all the command aliases, pipe delimited
        /// </summary>
        public string Name
        {
            get
            {
                return string.Join("|", names);
            }
        }
        
        public string Help { get; protected set; }
        public abstract int ArgCount { get; }

        protected abstract void Run(List<string> args);
        protected string[] names;

        protected CommandBase(string name)
        {            
            names = name.Split('|');
        }

        protected CommandBase(string name, string help) : this(name)
        {            
            Help = help;
        }

        public virtual void Execute(List<string> args)
        {
            try
            {
                Run(args);
            }
            catch (ArgumentException argEx)
            {
                string message = String.Format("{0}: {1}", GetSignature(), argEx.Message);
                throw new FormatException(message, argEx);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public virtual ICommand Implements(string command, List<string> args)
        {
            if (names.Contains(command) && ArgCount == args.Count)
            {
                return this;
            }

            return null;
        }

        public virtual bool Implements(string command, int argCount)
        {
            return (names.Contains(command) && ArgCount == argCount);
        }

        public bool Implements(string command)
        {
            return names.Contains(command);
        }

        public bool Equals(ICommand command)
        {
            CommandBase b = command as CommandBase;
            if( b == null )
            {
                return Implements(command.Name, 0);
            }
            else
            {
                return Implements(b.Name,b.ArgCount);
            }
        }

        protected string GetSignature()
        {
            StringBuilder s = new StringBuilder(Name);
            s.Append("(");
            
            Type[] args = GetType().GetGenericArguments();
            for (int i = 0; i < args.Length; ++i)
            {
                if (i > 0)
                {
                    s.Append(",");
                }
                s.Append(args[i].Name);
            }

            s.Append(")");
            return s.ToString();
        }

        public override string ToString()
        {
            StringBuilder output = new StringBuilder(GetSignature());
            if (!string.IsNullOrEmpty(Help))
            {
                output.Append(": ");
                output.Append(Help);
            }
            return output.ToString();
        }
    }

    public class Command : CommandBase
    {
        public override int ArgCount { get { return 0; } }
        private CommandLineAction Action { get; set; }
        public Command(string name, CommandLineAction action) : base(name)
        {
            if (action == null)
            {
                throw new ArgumentNullException("Must provide a CommandLineAction to perform", "action");
            }

            Action = action;
        }

        public Command(string name, string help, CommandLineAction action)
            : this(name, action)
        {
            Help = help;
        }

        protected override void Run(List<string> args)
        {
            Action();
        }        
    }

    public class Command<T> : CommandBase
    {
        public override int ArgCount { get { return 1; } }
        private CommandLineAction<T> Action { get; set; }
        public Command(string name, CommandLineAction<T> action) : base(name)
        {
            if (action == null)
            {
                throw new ArgumentNullException("Must provide a CommandLineAction to perform", "action");
            }

            Action = action;
            
        }

        public Command(string name, string help, CommandLineAction<T> action)
            : this(name, action)
        {
            Help = help;
        }

        protected override void Run(List<string> args)
        {
            Action(Converter.Parse<T>(args[0]));
        }
    }

    public class Command<T0, T1> : CommandBase
    {
        public override int ArgCount { get { return 2; } }

        private CommandLineAction<T0, T1> Action { get; set; }
        public Command(string name, CommandLineAction<T0, T1> action) : base(name)
        {
            if (action == null)
            {
                throw new ArgumentNullException("Must provide a CommandLineAction to perform", "action");
            }

            Action = action;            
        }

        public Command(string name, string help, CommandLineAction<T0,T1> action)
            : this(name, action)
        {
            Help = help;
        }

        protected override void Run(List<string> args)
        {
            T0 t0 = Converter.Parse<T0>(args[0]);
            T1 t1 = Converter.Parse<T1>(args[1]);
            Action(t0, t1);
        }
    }

    public class Command<T0, T1, T2> : CommandBase
    {
        public override int ArgCount { get { return 3; } }

        private CommandLineAction<T0, T1, T2> Action { get; set; }
        public Command(string name, CommandLineAction<T0, T1, T2> action) : base(name)
        {
            if (action == null)
            {
                throw new ArgumentNullException("Must provide a CommandLineAction to perform", "action");
            }

            Action = action;            
        }

        public Command(string name, string help, CommandLineAction<T0,T1,T2> action)
            : this(name, action)
        {
            Help = help;
        }

        protected override void Run(List<string> args)
        {
            T0 t0 = Converter.Parse<T0>(args[0]);
            T1 t1 = Converter.Parse<T1>(args[1]);
            T2 t2 = Converter.Parse<T2>(args[2]);
            Action(t0, t1, t2);
        }
    }

    public class Command<T0, T1, T2, T3> : CommandBase
    {
        public override int ArgCount { get { return 4; } }

        private CommandLineAction<T0, T1, T2, T3> Action { get; set; }
        public Command(string name, CommandLineAction<T0, T1, T2, T3> action) : base(name)
        {
            if (action == null)
            {
                throw new ArgumentNullException("Must provide a CommandLineAction to perform", "action");
            }

            Action = action;            
        }

        public Command(string name, string help, CommandLineAction<T0,T1,T2,T3> action)
            : this(name, action)
        {
            Help = help;
        }

        protected override void Run(List<string> args)
        {
            T0 t0 = Converter.Parse<T0>(args[0]);
            T1 t1 = Converter.Parse<T1>(args[1]);
            T2 t2 = Converter.Parse<T2>(args[2]);
            T3 t3 = Converter.Parse<T3>(args[3]);
            Action(t0, t1, t2, t3);
        }
    }

    public class Command<T0, T1, T2, T3, T4> : CommandBase
    {
        public override int ArgCount { get { return 5; } }

        private CommandLineAction<T0, T1, T2, T3, T4> Action { get; set; }
        public Command(string name, CommandLineAction<T0, T1, T2, T3, T4> action) : base(name)
        {
            if (action == null)
            {
                throw new ArgumentNullException("Must provide a CommandLineAction to perform", "action");
            }

            Action = action;            
        }

        public Command(string name, string help, CommandLineAction<T0,T1,T2,T3,T4> action)
            : this(name, action)
        {
            Help = help;
        }

        protected override void Run(List<string> args)
        {
            T0 t0 = Converter.Parse<T0>(args[0]);
            T1 t1 = Converter.Parse<T1>(args[1]);
            T2 t2 = Converter.Parse<T2>(args[2]);
            T3 t3 = Converter.Parse<T3>(args[3]);
            T4 t4 = Converter.Parse<T4>(args[4]);
            Action(t0, t1, t2, t3, t4);
        }
    }

    public class Command<T0, T1, T2, T3, T4, T5> : CommandBase
    {
        public override int ArgCount { get { return 6; } }

        private CommandLineAction<T0, T1, T2, T3, T4, T5> Action { get; set; }
        public Command(string name, CommandLineAction<T0, T1, T2, T3, T4, T5> action) : base(name)
        {
            if (action == null)
            {
                throw new ArgumentNullException("Must provide a CommandLineAction to perform", "action");
            }

            Action = action;            
        }

        public Command(string name, string help, CommandLineAction<T0, T1, T2, T3, T4, T5> action)
            : this(name, action)
        {
            Help = help;
        }

        protected override void Run(List<string> args)
        {
            T0 t0 = Converter.Parse<T0>(args[0]);
            T1 t1 = Converter.Parse<T1>(args[1]);
            T2 t2 = Converter.Parse<T2>(args[2]);
            T3 t3 = Converter.Parse<T3>(args[3]);
            T4 t4 = Converter.Parse<T4>(args[4]);
            T5 t5 = Converter.Parse<T5>(args[5]);
            Action(t0, t1, t2, t3, t4, t5);
        }
    }

    public class Command<T0, T1, T2, T3, T4, T5, T6> : CommandBase
    {
        public override int ArgCount { get { return 7; } }

        private CommandLineAction<T0, T1, T2, T3, T4, T5, T6> Action { get; set; }
        public Command(string name, CommandLineAction<T0, T1, T2, T3, T4, T5, T6> action) : base(name)
        {
            if (action == null)
            {
                throw new ArgumentNullException("Must provide a CommandLineAction to perform", "action");
            }

            Action = action;            
        }

        public Command(string name, string help, CommandLineAction<T0, T1, T2, T3, T4, T5, T6> action)
            : this(name, action)
        {
            Help = help;
        }

        protected override void Run(List<string> args)
        {
            T0 t0 = Converter.Parse<T0>(args[0]);
            T1 t1 = Converter.Parse<T1>(args[1]);
            T2 t2 = Converter.Parse<T2>(args[2]);
            T3 t3 = Converter.Parse<T3>(args[3]);
            T4 t4 = Converter.Parse<T4>(args[4]);
            T5 t5 = Converter.Parse<T5>(args[5]);
            T6 t6 = Converter.Parse<T6>(args[6]);
            Action(t0, t1, t2, t3, t4, t5, t6);
        }
    }

    public class Command<T0, T1, T2, T3, T4, T5, T6, T7> : CommandBase
    {
        public override int ArgCount { get { return 8; } }

        private CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7> Action { get; set; }
        public Command(string name, CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7> action) : base(name)
        {
            if (action == null)
            {
                throw new ArgumentNullException("Must provide a CommandLineAction to perform", "action");
            }

            Action = action;            
        }

        public Command(string name, string help, CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7> action)
            : this(name, action)
        {
            Help = help;
        }

        protected override void Run(List<string> args)
        {
            T0 t0 = Converter.Parse<T0>(args[0]);
            T1 t1 = Converter.Parse<T1>(args[1]);
            T2 t2 = Converter.Parse<T2>(args[2]);
            T3 t3 = Converter.Parse<T3>(args[3]);
            T4 t4 = Converter.Parse<T4>(args[4]);
            T5 t5 = Converter.Parse<T5>(args[5]);
            T6 t6 = Converter.Parse<T6>(args[6]);
            T7 t7 = Converter.Parse<T7>(args[7]);
            Action(t0, t1, t2, t3, t4, t5, t6, t7);
        }
    }

    public class Command<T0, T1, T2, T3, T4, T5, T6, T7, T8> : CommandBase
    {
        public override int ArgCount { get { return 9; } }

        private CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7, T8> Action { get; set; }
        public Command(string name, CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7, T8> action) : base(name)
        {
            if (action == null)
            {
                throw new ArgumentNullException("Must provide a CommandLineAction to perform", "action");
            }

            Action = action;            
        }

        public Command(string name, string help, CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7, T8> action)
            : this(name, action)
        {
            Help = help;
        }

        protected override void Run(List<string> args)
        {
            T0 t0 = Converter.Parse<T0>(args[0]);
            T1 t1 = Converter.Parse<T1>(args[1]);
            T2 t2 = Converter.Parse<T2>(args[2]);
            T3 t3 = Converter.Parse<T3>(args[3]);
            T4 t4 = Converter.Parse<T4>(args[4]);
            T5 t5 = Converter.Parse<T5>(args[5]);
            T6 t6 = Converter.Parse<T6>(args[6]);
            T7 t7 = Converter.Parse<T7>(args[7]);
            T8 t8 = Converter.Parse<T8>(args[8]);
            Action(t0, t1, t2, t3, t4, t5, t6, t7, t8);
        }
    }

    public class Command<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> : CommandBase
    {
        public override int ArgCount { get { return 10; } }

        private CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> Action { get; set; }
        public Command(string name, CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> action) : base(name)
        {
            if (action == null)
            {
                throw new ArgumentNullException("Must provide a CommandLineAction to perform", "action");
            }

            Action = action;            
        }

        public Command(string name, string help, CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> action)
            : this(name, action)
        {
            Help = help;
        }

        protected override void Run(List<string> args)
        {
            T0 t0 = Converter.Parse<T0>(args[0]);
            T1 t1 = Converter.Parse<T1>(args[1]);
            T2 t2 = Converter.Parse<T2>(args[2]);
            T3 t3 = Converter.Parse<T3>(args[3]);
            T4 t4 = Converter.Parse<T4>(args[4]);
            T5 t5 = Converter.Parse<T5>(args[5]);
            T6 t6 = Converter.Parse<T6>(args[6]);
            T7 t7 = Converter.Parse<T7>(args[7]);
            T8 t8 = Converter.Parse<T8>(args[8]);
            T9 t9 = Converter.Parse<T9>(args[9]);
            Action(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9);
        }
    }

    public delegate void CommandLineAction();
    public delegate void CommandLineAction<T>(T arg);
    public delegate void CommandLineAction<T0, T1>(T0 arg0, T1 arg1);
    public delegate void CommandLineAction<T0, T1, T2>(T0 arg0, T1 arg1, T2 arg2);
    public delegate void CommandLineAction<T0, T1, T2, T3>(T0 arg0, T1 arg1, T2 arg2, T3 arg3);
    public delegate void CommandLineAction<T0, T1, T2, T3, T4>(T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4);
    public delegate void CommandLineAction<T0, T1, T2, T3, T4, T5>(T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
    public delegate void CommandLineAction<T0, T1, T2, T3, T4, T5, T6>(T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
    public delegate void CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7>(T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
    public delegate void CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7, T8>(T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
    public delegate void CommandLineAction<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>(T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9);  
}
